package com.hxzy.shop.auth;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.hxzy.shop.cachedao.UserDto;
import com.nimbusds.jose.JWSObject;
import com.hxzy.shop.cachedao.AuthCacheService;
import com.hxzy.shop.constant.AuthConstant;
import com.hxzy.shop.constant.RedisConstant;
import com.hxzy.shop.properties.IgnoreUrlProperties;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.authorization.AuthorizationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.net.URI;
import java.text.ParseException;
import java.util.List;


/**
 * 检查令牌有效性(redis)
 */
@Component
public class AuthorizationManager implements ReactiveAuthorizationManager<AuthorizationContext> {
    @Resource
    private IgnoreUrlProperties ignoreUrlProperties;

    /**
     * redis服务接口
     */
    @Resource
    private AuthCacheService authCacheService;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> mono, AuthorizationContext authorizationContext) {
        //得到httpRequest请求
        ServerHttpRequest request = authorizationContext.getExchange().getRequest();
        //得到请求地址  http://localhost:8052/headquarters/auth/captchaCode
        URI uri = request.getURI();
        //只包含路径 /headquarters/auth/captchaCode
        String path = uri.getPath();
        //路径解析顺
        PathMatcher pathMatcher = new AntPathMatcher();
        //得到令牌  ，前端发过来的Header("Token")相当于 fresh_token
        String appletToken = request.getHeaders().getFirst(AuthConstant.APPLET_JWT_TOKEN_HEADER);

        //为令牌增加24小时
        authCacheService.renewalAppletUserInfo(appletToken);

        //白名单路径直接放行
        List<String> ignoreUrls = ignoreUrlProperties.getUrls();
        for (String ignoreUrl : ignoreUrls) {
            if (pathMatcher.match(ignoreUrl, path)) {
                return Mono.just(new AuthorizationDecision(true));
            }
        }

        //对应跨域的预检请求直接放行, 判断springboot支不支持跨请请求
        if (request.getMethod() == HttpMethod.OPTIONS) {
            return Mono.just(new AuthorizationDecision(true));
        }
        //获取用户登录信息
        try {
            // 获取请求 header("Authorization")   加密的令牌，解析，并且redis查询
            String token = request.getHeaders().getFirst(AuthConstant.JWT_TOKEN_HEADER);
            if (StrUtil.isEmpty(token)) {
                // RestAuthenticationEntryPoint
                return Mono.just(new AuthorizationDecision(false));
            }

            if (!token.startsWith(AuthConstant.JWT_TOKEN_PREFIX)) {
                return Mono.just(new AuthorizationDecision(false));
            }

            String realToken = token.replace(AuthConstant.JWT_TOKEN_PREFIX, "");
            //解析令牌   RSA  非对称加密算法
            JWSObject jwsObject = JWSObject.parse(realToken);
            //得到用户信息
            String userStr = jwsObject.getPayload().toString();
            UserDto userDto = JSONUtil.toBean(userStr, UserDto.class);

            String clientId = userDto.getClient_id();
            //总商店平台，只允许clientId=Admin和Admin_Slave和back_admin
            if (!AuthConstant.ADMIN_CLIENT_ID.equals(clientId) && !AuthConstant.ADMIN_SLAVE_CLIENT_ID.equals(clientId) &&!AuthConstant.BACK_ADMIN_CLIENT_ID.equals(clientId)) {
                return Mono.just(new AuthorizationDecision(false));
            }

            String account = userDto.getUser_name();
            String key=null;
            //商家登录解析 ,redisKey
            if (ObjectUtil.isNotEmpty(userDto.getMerchantId())){
                 key = RedisConstant.AUTHORIZATION + clientId + ":" + account+String.valueOf(userDto.getMerchantId());
            }else {
                 key = RedisConstant.AUTHORIZATION + clientId + ":" + account;
            }
            //查询redis中是否存
            boolean bool = authCacheService.checkAccount(key, realToken);
            if (!bool) {
                return Mono.just(new AuthorizationDecision(false));
            }

            //保存请求后台用户信息到缓存中，方便再平台端获取请求用户
            authCacheService.saveUserDto(token,userDto);
        } catch (ParseException e) {
            return Mono.just(new AuthorizationDecision(false));
        }
        return Mono.just(new AuthorizationDecision(true));
    }
}
